/**
 * 
 */
package com.dalonedrau.jogl.opengl.ui;

import com.dalonedrau.jogl.engine.GameApplet;
import com.dalonedrau.jogl.opengl.GameFont;
import com.dalonedrau.jogl.opengl.sprite.FontsheetObject;
import com.dalonedrau.vectors.Vector2;

/**
 * @author Donald
 */
public final class LineOfText {
	/** the maximum number of characters that could possibly be rendered. */
	public static final int	MAX_CHARS	= 94;
	/** flag indicating the line has been configured and is ready to render. */
	private boolean			exists;
	/**
	 * stores the index last used when a character's x-offset was recorded in
	 * the positions array. for example, if the positions array has offsets for
	 * four instances of the letter 's', then the last used index for 's' would
	 * be 3.
	 */
	private int[]			lastUsedIndex;
	/** the position of the line relative to its parent. */
	private Vector2			linePosition;
	/** the total number of characters in the line. */
	private int				numChars	= 0;
	/**
	 * the x-offset of each character in the line is stored in a two dimensional
	 * array. each possible character has its own float array assigned to store
	 * the x-offset for each instance of the character to mark where in the line
	 * it is rendered at.
	 */
	private float[][]		positions;
	/**
	 * Creates a new instance of {@link LineOfText}.
	 */
	public LineOfText() {
		exists = false;
		// create the positions array
		positions = new float[LineOfText.MAX_CHARS][];
		for (int i = 0; i < positions.length; i++) {
			positions[i] = new float[0];
		}
		// create array for last used index
		lastUsedIndex = new int[LineOfText.MAX_CHARS];
		for (int i = 0; i < lastUsedIndex.length; i++) {
			lastUsedIndex[i] = 0;
		}
		linePosition = new Vector2();
	}
	/**
	 * Adds a character to the line.
	 * @param c the character being added
	 * @param x the x-offset where the character will be rendered
	 */
	private void addCharacter(final char c, final float x) throws Exception {
		if (debug) {
			System.out.println("addCharacter(" + c + "," + x);
		}
		// the array index of the new position equals the character's ascii
		// value minus the ascii value of '!' (33). for 'A' (65) its index
		// would be 22.
		int characterIndex = c - ' ';
		// increment the number of values stored in the array
		lastUsedIndex[characterIndex] = lastUsedIndex[characterIndex] + 1;
		if (lastUsedIndex[characterIndex] < 0) {
			lastUsedIndex[characterIndex] = 0;
		}
		// check the positions array to see if there's room
		if (positions[characterIndex].length <= lastUsedIndex[characterIndex]) {
			// not enough room in array. make room
			float[] dest = new float[lastUsedIndex[characterIndex] + 1];
			System.arraycopy(
					positions[characterIndex],
					0,
					dest,
					0,
					positions[characterIndex].length
					);
			positions[characterIndex] = dest;
			dest = null;
		}
		// array has enough room. set the value
		try {
			positions[characterIndex][lastUsedIndex[characterIndex]] = x;
		} catch (Exception ex) {
			try {
				positions[characterIndex][lastUsedIndex[characterIndex]] = x;
				throw ex;
			} catch (Exception ex2) {
				throw(ex2);
			}
		}
		numChars++;
	}
	public void clear() {
		linePosition.set(0, 0);
		for (int i = 0; i < lastUsedIndex.length; i++) {
			lastUsedIndex[i] = -1;
		}
		numChars = 0;
		exists = false;
	}
	/**
	 * Determines if the {@link LineOfText} exists and is ready for rendering.
	 * @return true if the line has formatted text to render; false otherwise
	 */
	public boolean exists() {
		return exists;
	}
	private boolean debug;
	/**
	 * Gets the debug 
	 * @return {@link boolean}
	 */
	public boolean isDebug() {
		return debug;
	}
	/**
	 * Sets the debug
	 * @param debug the debug to set
	 */
	public void setDebug(boolean debug) {
		this.debug = debug;
	}
	/**
	 * Adds text to the line and formats it based on the alignment and width.
	 * @param width the width in which the text will be formatted
	 * @param alignment the text alignment
	 * @param text the text
	 * @param fontRefId the id of the font used
	 * @throws Exception if an error occurs
	 */
	public void formatText(final int width, final int alignment,
			final String text, final int fontRefId) throws Exception {
		if (debug) {
			System.out.println("formatText(" + width + "," + alignment + ", " 
					+ text);
		}
		clear();
		FontsheetObject font = GameFont.getInstance().getFontById(fontRefId);
		switch (alignment) {
		case GuiComponent.TEXT_JUSTIFY:
			// get the width without spaces
			int textWidth = font.getTextWidthNoSpaces(text);
			// count the number of spaces
			int numSpaces = 0;
			String[] split = text.split(" ");
			numSpaces = split.length - 1;
			if (numSpaces <= 0) {
				// no spaces; just center the text.
				// move x to the left offset
				float x = (width - textWidth) / 2;
				// iterate through the text going from left to right
				for (int i = 0; i < text.length(); i++) {
					char c = text.charAt(i);
					font.setGlyph(c);
					addCharacter(c, x);
					x += font.getCharWidth();
				}
			} else {
				float x = 0;
				float spaceWidth = (width - textWidth) / numSpaces;
				// iterate through the text going from left to right
				for (int out = 0; out < split.length; out++) {
					// add a word to the line
					for (int in = 0; in < split[out].length(); in++) {
						char c = split[out].charAt(in);
						font.setGlyph(c);
						addCharacter(c, x);
						x += font.getCharWidth();
					}
					// after the word, add justified space
					addCharacter(' ', x);
					x += spaceWidth;
				}
			}
			break;
		case GuiComponent.RIGHT:
			// move x to far right side
			float x = width;
			// iterate through the text going backwards
			for (int i = text.length() - 1; i >= 0; i--) {
				char c = text.charAt(i);
				font.setGlyph(c);
				x -= font.getCharWidth();
				/*
				 * if (c != ' ') { addCharacter(c, x); }
				 */
				addCharacter(c, x);
			}
			break;
		case GuiComponent.CENTER:
			// size up the text
			textWidth = font.getTextWidth(text);
			// move x to the left offset
			x = (width - textWidth) / 2;
			// iterate through the text going from left to right
			for (int i = 0; i < text.length(); i++) {
				char c = text.charAt(i);
				font.setGlyph(c);
				/*
				 * if (c != ' ') { addCharacter(c, x); }
				 */
				addCharacter(c, x);
				x += font.getCharWidth();
			}
			break;
		default:
			// add characters going from left to right
			x = 0;
			for (int i = 0; i < text.length(); i++) {
				char c = text.charAt(i);
				font.setGlyph(c);
				/*
				 * if (c != ' ') { addCharacter(c, x); }
				 */
				addCharacter(c, x);
				x += font.getCharWidth();
			}
			break;
		}
		exists = true;
	}
	public int getCharFrequency(final int index) {
		return lastUsedIndex[index] + 1;
	}
	public int getNumberOfCharacters() {
		return numChars;
	}
	/**
	 * Gets the x-position of a specific character in the line.
	 * @param character the character rendered
	 * @param index the index of the instance of the character. a character will
	 *        most likely be repeated several times in a line ('e', or 'h'), and
	 *        each index has the x-offset for each repeated instance
	 * @return <code>float</code>
	 */
	public float getXPosition(final int character, final int index) {
		return positions[character][index];
	}
	/**
	 * Gets the y-position of the line.
	 * @return <code>float</code>
	 */
	public float getYPosition() {
		return (float) linePosition.getY();
	}
	/**
	 * Gets the x-position of the line.
	 * @return <code>float</code>
	 */
	public float getXPosition() {
		return (float) linePosition.getX();
	}
	public void setPosition(final float x, final float y) {
		linePosition.set(x, y);
		exists = true;
	}
	public void setPosition(final Vector2 v) {
		linePosition.set(v);
		exists = true;
	}
}
